/*
 * This file is part of Shopping Lists Organizer software
 *
 * Copyright (C) 2011 Denis Kvita <dkvita@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 */

#include "shoppinglisteditor.h"

#include <QtGui>
#include <QtSql>

#include "purchase.h"
#include "calendar.h"
#include "shopdelegate.h"

shoppingListEditor::shoppingListEditor(QWidget *parent)
	: QWidget(parent)
{
	setWindowTitle(tr("Shopping List Editor"));

	activeListIdx= 0;

	QSettings cfg(qApp->property("dataDir").toString()+"/settings.ini", QSettings::IniFormat);

	purchaseD= new purchase;

	purchaseP= new QSortFilterProxyModel;
	purchaseP->setSourceModel(purchaseD);

	purchaseV= new QTableView;
	purchaseV->setItemDelegate(new shopDelegate());
	purchaseV->setModel(purchaseP);
	purchaseV->verticalHeader()->setDefaultSectionSize(22);
	purchaseV->verticalHeader()->setMovable(true);
	purchaseV->horizontalHeader()->setFixedHeight(22);
	purchaseV->horizontalHeader()->setSortIndicatorShown(true);
	purchaseV->horizontalHeader()->setMovable(true);
	purchaseV->horizontalHeader()->setContextMenuPolicy(Qt::ActionsContextMenu);
	purchaseV->horizontalHeader()->setTextElideMode(Qt::ElideMiddle);
	purchaseV->setSortingEnabled(true);
	purchaseV->setEditTriggers(QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::AnyKeyPressed);//AllEditTriggers);
	purchaseV->setSelectionBehavior(QAbstractItemView::SelectItems);
	purchaseV->setSelectionMode(QAbstractItemView::SingleSelection);


	QByteArray hDat= QByteArray::fromBase64(cfg.value("/listEditor/header").toByteArray());
	if(!hDat.isEmpty()){
		hDat= qUncompress(hDat);
		if(!hDat.isEmpty()) purchaseV->horizontalHeader()->restoreState(hDat);
	}

	QAction *a;
	for(int i=0; i<purchaseD->columnCount(); i++){
		a= new QAction(purchaseD->headerData(i,Qt::Horizontal).toString(),purchaseV->horizontalHeader());
		a->setData(i);
		a->setCheckable(true);
		a->setChecked(purchaseV->horizontalHeader()->isSectionHidden(i) ? Qt::Unchecked : Qt::Checked );
		connect(a, SIGNAL(triggered(bool)), this, SLOT(onHeaderSectionShow(bool)));
		purchaseV->horizontalHeader()->addAction(a);
	}

	listDate= new QDateEdit;
	listDate->setCalendarPopup(true);
	listDate->setCalendarWidget(new Calendar());
	if(QLocale::system().country() == QLocale::RussianFederation || QLocale::system().country() == QLocale::Ukraine) listDate->calendarWidget()->setFirstDayOfWeek(Qt::Monday);
	listDate->setDate(cfg.value("/listEditor/date",QDate::currentDate()).toDate());
	connect(listDate, SIGNAL(dateChanged(QDate)), this, SLOT(onDateChanged(QDate)));

	activeListDate= listDate->date();

	shopListV= new QComboBox;
	shopListV->setEditable(true);
	shopListV->setInsertPolicy(QComboBox::InsertAtCurrent);
	shopListV->setDuplicatesEnabled(false);
	shopListV->completer()->setCompletionMode(QCompleter::PopupCompletion);
	shopListV->setToolTip(tr("enter a list title and press Enter key"));
	connect(shopListV->lineEdit(), SIGNAL(textEdited(QString)), this, SLOT(onListNameEdited(QString)));
	connect(shopListV->lineEdit(), SIGNAL(editingFinished()), this, SLOT(onListRenamed()));
	connect(shopListV, SIGNAL(currentIndexChanged(int)), this, SLOT(onListChanged(int)));

	QLabel *sumPlanL= new QLabel(tr("Total planned: "));
	sumPlanL->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
	sumPlanV= new QLabel;
	QFont bFnt= font();
	bFnt.setBold(true);
	sumPlanV->setFont(bFnt);

	QLabel *sumBuyL= new QLabel(tr("Total bought: "));
	sumBuyL->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
	sumBuyV= new QLabel;
	sumBuyV->setFont(bFnt);

	addPurchase= new QToolButton;
	addPurchase->setIcon(QIcon(":/img/add.png"));
	addPurchase->setToolTip(tr("add purchase"));
	addPurchase->setAutoRaise(true);
	addPurchase->setEnabled(false);
	connect(addPurchase, SIGNAL(released()), this, SLOT(onAddPurchase()));

	remPurchase= new QToolButton;
	remPurchase->setIcon(QIcon(":/img/rem.png"));
	remPurchase->setToolTip(tr("delete purchase"));
	remPurchase->setAutoRaise(true);
	remPurchase->setEnabled(false);
	connect(remPurchase, SIGNAL(released()), this, SLOT(onRemovePurchase()));

	remList= new QToolButton;
	remList->setIcon(QIcon(":/img/listRem.png"));
	remList->setToolTip(tr("delete current shopping list"));
	remList->setAutoRaise(true);
	remList->setEnabled(false);
	connect(remList, SIGNAL(released()), this, SLOT(onRemoveList()));

	printList= new QToolButton;
	printList->setIcon(QIcon(":/img/printer.png"));
	printList->setToolTip(tr("print current shopping list"));
	printList->setAutoRaise(true);
	printList->setEnabled(false);
	connect(printList, SIGNAL(released()), this, SLOT(onPrintList()));

	connect(this, SIGNAL(guiControlsSwitch(bool)), addPurchase, SLOT(setEnabled(bool)));
	connect(this, SIGNAL(guiControlsSwitch(bool)), remPurchase, SLOT(setEnabled(bool)));
	connect(this, SIGNAL(guiControlsSwitch(bool)), remList, SLOT(setEnabled(bool)));
	connect(this, SIGNAL(guiControlsSwitch(bool)), printList, SLOT(setEnabled(bool)));
	connect(purchaseD, SIGNAL(totalPlanChanged(double)), this, SLOT(onTotalPlanChanged(double)));
	connect(purchaseD, SIGNAL(totalBuyChanged(double)), this, SLOT(onTotalBuyChanged(double)));

	QHBoxLayout *lstLt= new QHBoxLayout;
	lstLt->addWidget(addPurchase);
	lstLt->addWidget(remPurchase);
	lstLt->addWidget(listDate);
	lstLt->addWidget(shopListV,1);
	lstLt->addWidget(remList);
	lstLt->addWidget(printList);
	lstLt->setMargin(0);
	lstLt->setSpacing(2);

	QHBoxLayout *sumLt= new QHBoxLayout;
	sumLt->addWidget(sumPlanL);
	sumLt->addWidget(sumPlanV);
	sumLt->addSpacing(20);
	sumLt->addWidget(sumBuyL);
	sumLt->addWidget(sumBuyV);
	sumLt->addStretch(1);
	sumLt->setMargin(0);
	sumLt->setSpacing(2);

	QVBoxLayout *mlt= new QVBoxLayout;
	mlt->addLayout(lstLt);
	mlt->addWidget(purchaseV,1);
	mlt->addLayout(sumLt);
	mlt->setMargin(3);

	setLayout(mlt);

	QAction *act= new QAction(tr("new"),this);
	act->setShortcut(QKeySequence(Qt::Key_Insert));
	connect(act,SIGNAL(triggered()), this, SLOT(onAddPurchase()));
	addAction(act);
}

shoppingListEditor::~shoppingListEditor()
{

}

void shoppingListEditor::onDateChanged(const QDate& date)
{
	restore();
	activeListDate= date;
}

void shoppingListEditor::onListNameEdited(const QString &text)
{
	if(text == tr("<new shop list>") || shopListV->currentIndex() == 0){
		emit guiControlsSwitch(false);
	}else{
		int idx= shopListV->findText(text);
		if(idx>0) return;
		if(shopListV->currentIndex() != 0) emit guiControlsSwitch(true);
	}
}

void shoppingListEditor::onListRenamed()
{
	if(shopListV->currentIndex() == 0){
		if(shopListV->currentText() == tr("<new shop list>"))
			return;

		for(int i=1; i<shopListV->count(); i++){
			if(shopListV->itemText(i) == shopListV->lineEdit()->text()){
				shopListV->setCurrentIndex(i);
				shopListV->setItemText(0, tr("<new shop list>"));
				return;
			}
		}
		QString newName= shopListV->currentText().trimmed();//shopListV->itemText(0);
		shopListV->addItem(newName);
		shopListV->setItemText(0, tr("<new shop list>"));
		shopListV->setCurrentIndex(shopListV->findText(newName));
	}else{
		if(shopListV->currentIndex() == activeListIdx){
			shopListV->setItemText(shopListV->currentIndex(), shopListV->currentText().trimmed());
			QSqlDatabase dbc= QSqlDatabase::database("CN");
			QSqlQuery q(dbc);
			q.prepare(QString("UPDATE purchase SET pList=:N WHERE pDate=:D AND pList=:L"));
			q.bindValue(":N", shopListV->currentText().trimmed());
			q.bindValue(":D", listDate->date().toString("yyyy-MM-dd"));
			q.bindValue(":L", activeListName);
			if(!q.exec()) return;
			q.prepare(QString("UPDATE listSettings SET pList=:N WHERE pDate=:D AND pList=:L"));
			q.bindValue(":N", shopListV->currentText().trimmed());
			q.bindValue(":D", listDate->date().toString("yyyy-MM-dd"));
			q.bindValue(":L", activeListName);
			if(!q.exec()) return;
			purchaseD->setList(shopListV->currentText().trimmed());
		}
	}
}

void shoppingListEditor::onListChanged(int index)
{

	if(activeListIdx == shopListV->currentIndex()) return;
	// save headerView settings
	saveVisualSettings();

	activeListIdx= shopListV->currentIndex();
	activeListName= shopListV->currentText();

	purchaseD->clear();
	if(shopListV->currentIndex() <= 0){
		emit guiControlsSwitch(false);
		return;
	}

	// restore headerView settings
	restoreVisualSettings();
	purchaseD->restore(listDate->date(), shopListV->currentText());
	emit guiControlsSwitch(true);
}

void shoppingListEditor::onAddPurchase()
{
	if(shopListV->currentIndex() == 0){
		emit guiControlsSwitch(false);
		return;
	}
	QModelIndex idx= purchaseD->addEmpty();
	if(idx.isValid()){
		purchaseV->clearSelection();
		purchaseV->setCurrentIndex(purchaseP->mapFromSource(idx));
		purchaseV->scrollTo(purchaseP->mapFromSource(idx));
	}
}

void shoppingListEditor::onRemovePurchase()
{
	if(shopListV->currentIndex() == 0){
		emit guiControlsSwitch(false);
		return;
	}
	if(!purchaseV->selectionModel()->hasSelection()) return;
	purchaseD->rem(selectedPurchase());
}

void shoppingListEditor::onRemoveList()
{
	if(shopListV->currentIndex() == 0) return;

	if(QMessageBox::question(this,tr("Delete list"),
							 tr("Do you want to delete the list of purchases <b>'")
							 +shopListV->currentText()+tr("'</b>?"),
							 QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) return;

	QSqlDatabase dbc= QSqlDatabase::database("CN", false);
	if(!dbc.isOpen()){
		qCritical()<<"Database is unaccessible!";
		return;
	}

	QString listName= shopListV->currentText();
	QSqlQuery q(dbc);
	q.prepare("DELETE FROM purchase WHERE pDate=:D AND pList=:LST");
	q.bindValue(":D",listDate->date().toString("yyyy-MM-dd"));
	q.bindValue(":LST",listName);
	if(!q.exec()){
		qCritical()<<"Can't remove list(error during purchase(s) deletion)!";
	}

	// delete list`s visual settings
	q.prepare("DELETE FROM listSettings WHERE pDate=:D AND pList=:LST");
	q.bindValue(":D",listDate->date().toString("yyyy-MM-dd"));
	q.bindValue(":LST",listName);
	if(!q.exec()){
		qCritical()<<"Can't remove list(error during list`s visual settings deletion)!";
	}
	activeListIdx= -1;
	activeListName= "";
	purchaseD->clear();
	shopListV->removeItem(shopListV->findText(listName));
}

int shoppingListEditor::selectedPurchase()
{
	return purchaseP->mapToSource(purchaseV->selectionModel()->selectedIndexes().at(0)).row();
}

void shoppingListEditor::saveSettings()
{
	QSettings cfg(qApp->property("dataDir").toString()+"/settings.ini", QSettings::IniFormat);
	cfg.setValue("/listEditor/date",listDate->date());
	cfg.setValue("/listEditor/list",shopListV->currentText());
	cfg.setValue("/listEditor/header",qCompress(purchaseV->horizontalHeader()->saveState()).toBase64());
	saveVisualSettings();
}

void shoppingListEditor::saveVisualSettings(){
	if(activeListIdx <= 0) return;

	QByteArray hs= qCompress(purchaseV->horizontalHeader()->saveState()).toBase64();
	int hid= qChecksum(hs.data(),hs.size());

	QSqlQuery q(QSqlDatabase::database("CN"));
	q.prepare("SELECT hid FROM headerState WHERE hid=:H");
	q.bindValue(":H", hid);
	if(!q.exec()) return;
	if(!q.next()){
		q.prepare("INSERT OR IGNORE INTO headerState(hid,state) (:H,:S");
		q.bindValue(":H", hid);
		q.bindValue(":S", hs);
		q.exec();
	}

	q.prepare("SELECT oid,val FROM listCfg WHERE pDate=:DT AND pList=:L AND attr=:A");
	q.bindValue(":DT", activeListDate.toString("yyyy-MM-dd"));
	q.bindValue(":L", activeListName);
	q.bindValue(":A", "headerState");
	if(q.exec() && q.next()){
		if(q.value(1).toInt()==hid) return;

		q.prepare("UPDATE listCfg SET val=:H "
				  " WHERE pDate=:DT AND pList=:L AND attr=:A");
		q.bindValue(":H", hid);
		q.bindValue(":DT", activeListDate.toString("yyyy-MM-dd"));
		q.bindValue(":L", activeListName);
		q.bindValue(":A", "headerState");
		q.exec();
	}else{
		q.prepare("INSERT INTO listCfg(pDate,pList,attr,val) "
				  " VALUES(:DT,:L,:A,:V");
		q.bindValue(":DT", activeListDate.toString("yyyy-MM-dd"));
		q.bindValue(":L", activeListName);
		q.bindValue(":A", "headerState");
		q.bindValue(":V", hid);
		q.exec();
	}
}

void shoppingListEditor::restoreVisualSettings(){
	if(activeListIdx <= 0) return;
	QSqlQuery q(QSqlDatabase::database("CN"));
	q.prepare("SELECT state FROM listCfg,headerState "
			  "WHERE pDate=:D AND pList=:L AND attr=:A AND val=hid");
	q.bindValue(":D", listDate->date().toString("yyyy-MM-dd"));
	q.bindValue(":L", activeListName);
	q.bindValue(":A", "headerState");
	if(q.exec()){
		if(q.next()){
			QByteArray b= QByteArray::fromBase64(q.value(0).toString().toAscii());
			if(!b.isEmpty()){
				b= qUncompress(b);
				if(!b.isEmpty()) purchaseV->horizontalHeader()->restoreState(b);
			}
		}
	}
	bool ok;
	foreach(QAction *a, purchaseV->horizontalHeader()->actions()){
		ok= false;
		int i= a->data().toInt(&ok);
		if(ok) a->setChecked(purchaseV->horizontalHeader()->isSectionHidden(i) ? Qt::Unchecked : Qt::Checked );
	}
}

void shoppingListEditor::onPrintList()
{
	QPrinter *printer= new QPrinter(QPrinter::PrinterResolution);
	QPrintDialog printDialog(printer, this);
	if(printDialog.exec() == QDialog::Accepted){
		purchaseD->print(printer);
	}
}

void shoppingListEditor::onTotalPlanChanged(double newTotal)
{
	sumPlanV->setText(QString::number(newTotal,'f',2));
}

void shoppingListEditor::onTotalBuyChanged(double newTotal)
{
	sumBuyV->setText(QString::number(newTotal,'f',2));
}

void shoppingListEditor::updateData()
{
	QString listName= activeListName;
	shopListV->clear();
	shopListV->addItem(tr("<new shop list>"));

	QSqlDatabase dbc;
	if(!QSqlDatabase::contains("CN")) return;
	dbc= QSqlDatabase::database("CN", false);
	if(!dbc.isOpen()){
		qCritical()<<"Database is unaccessible!";
		return;
	}

	QSqlQuery q(dbc);
	q.prepare("SELECT DISTINCT pList FROM purchase WHERE pDate=:DT");
	q.bindValue(":DT", listDate->date().toString("yyyy-MM-dd"));
	if(!q.exec()){
		qCritical()<<"Query error while retrive lists from DB!";
	}
	while(q.next()) shopListV->addItem(q.value(0).toString());

	if(shopListV->count()>1){
		if(shopListV->findText(listName)<1) shopListV->setCurrentIndex(1);
		else shopListV->setCurrentIndex(shopListV->findText(listName));
	}

	activeListIdx= shopListV->currentIndex();
	activeListName= shopListV->currentText();
	purchaseD->restore(listDate->date(), shopListV->currentText());

	restoreVisualSettings();
}

void shoppingListEditor::restore()
{
	QSettings cfg(qApp->property("dataDir").toString()+"/settings.ini", QSettings::IniFormat);
	shopListV->clear();
	shopListV->addItem(tr("<new shop list>"));

	QSqlDatabase dbc;
	if(!QSqlDatabase::contains("CN")) return;
	dbc= QSqlDatabase::database("CN", false);
	if(!dbc.isOpen()){
		qCritical()<<"Database is unaccessible!";
		return;
	}

	QSqlQuery q(dbc);
	q.prepare("SELECT DISTINCT pList FROM purchase WHERE pDate=:DT");
	q.bindValue(":DT", listDate->date().toString("yyyy-MM-dd"));
	if(!q.exec()){
		qCritical()<<"Query error while retrive lists from DB!";
	}
	while(q.next()) shopListV->addItem(q.value(0).toString());

	if(shopListV->count()>1){
		if(shopListV->findText(cfg.value("/listEditor/list").toString())<1) shopListV->setCurrentIndex(1);
		else shopListV->setCurrentIndex(shopListV->findText(cfg.value("/listEditor/list").toString()));
	}

	activeListIdx= shopListV->currentIndex();
	activeListName= shopListV->currentText();

	purchaseD->restore(listDate->date(), shopListV->currentText());

	restoreVisualSettings();
}

void shoppingListEditor::onHeaderSectionShow(bool isShow)
{
	QAction *act= qobject_cast<QAction*>(sender());
	if(act == 0) return;
	purchaseV->horizontalHeader()->setSectionHidden(act->data().toInt(),!isShow);
}
